import { Constraint, EventsRes, PickFrom } from '@amcax/base';
import { ConstraintIcon } from '@app-base/common/utils/renderObjects.ts/ConstraintIcon';
import { ctx2, userEvents } from '@app-cad/module';
import {
  ConstraintIconEvents,
  constraintIconRenderer,
} from './ConstraintIconRenderer';
import { Intersection, Sprite } from 'three-legacy';
import { Picker2D, PickerMgr } from '@amcax/renderer';

export class ConstraintIconPicker {
  selectedIcons: ConstraintIcon[] = [];
  hoveredIcon: ConstraintIcon;

  private getPickedObjOfIntersection(intersection: Intersection) {
    const pickedObj = intersection?.object;

    if (pickedObj == null) {
      return null;
    }

    if (pickedObj instanceof Sprite) {
      return pickedObj.parent as ConstraintIcon;
    }

    return pickedObj as ConstraintIcon;
  }

  private getIcons() {
    return Array.from(constraintIconRenderer.renderedObjects).flatMap(
      (v) => v?.children ?? [],
    ) as ConstraintIcon[];
  }

  private raycast(event: EventsRes<PointerEvent>) {
    const result = ctx2.viewer.sceneSetup.raycast(event.mouse, this.getIcons());

    const allPickedObj = result.map(this.getPickedObjOfIntersection);

    return {
      pickedObj: allPickedObj[0],
      allPickedObj,
    };
  }

  pick(event: EventsRes<PointerEvent>) {
    const { pickedObj, allPickedObj } = this.raycast(event);

    if (!allPickedObj.includes(this.hoveredIcon)) {
      if (pickedObj != null) {
        pickedObj.hover();
      }

      if (pickedObj !== this.hoveredIcon) {
        this.hoveredIcon?.hover(false);
      }
      this.hoveredIcon = pickedObj;

      ctx2.requestRender();
    }

    return this.hoveredIcon;
  }

  unhover() {
    this.hoveredIcon?.hover(false);
    this.hoveredIcon = null;
  }

  clearSelect() {
    this.selectedIcons.forEach((icon) => icon.select(false));
    this.selectedIcons = [];
  }

  select({ event }: EventsRes<PointerEvent>) {
    const pickedObj = this.hoveredIcon;

    if (event.ctrlKey) {
      // 左键，普通逻辑
      if (event.button === 0) {
        if (this.selectedIcons.includes(pickedObj)) {
          pickedObj.select(false);
          this.selectedIcons = this.selectedIcons.filter(
            (i) => i !== pickedObj,
          );
        } else if (pickedObj) {
          pickedObj.select();
          this.selectedIcons.push(pickedObj);
        }
      } else {
        if (pickedObj != null && !this.selectedIcons.includes(pickedObj)) {
          this.clearSelect();
          this.selectedIcons = [pickedObj];
          pickedObj.select();
        }
      }
    } else {
      this.clearSelect();
      if (pickedObj) {
        pickedObj.select();
        this.selectedIcons = [pickedObj];
      }
    }

    userEvents.emit(ConstraintIconEvents.SelectFromCanvas, {
      constraints: this.selectedIcons.map((i) => i.constraintTag.constraint),
      from: PickFrom.Canvas,
    });

    this.highlightConstrainRelatedObjects();
    setTimeout(() => {
      ctx2.requestRender();
    });
  }

  highlightConstrainRelatedObjects() {
    const relatedIds = new Set<string>();
    this.selectedIcons.forEach((i) => {
      relatedIds.add(i.constraintTag.constraint.itemid);
      relatedIds.add(i.constraintTag.constraint.target);
      relatedIds.add(i.constraintTag.constraint.axis);
    });

    PickerMgr.getPicker<Picker2D>('2d').setHighlightModels(
      Array.from(relatedIds),
    );
  }

  selectByData(data: Constraint[], from: PickFrom = PickFrom.Canvas) {
    this.clearSelect();
    const result = this.getIcons().filter((i) =>
      data.some((target) =>
        constraintIconRenderer.isSameConstraint(
          i.constraintTag.constraint,
          target,
        ),
      ),
    );
    this.selectedIcons = result;
    this.selectedIcons.forEach((i) => i.select());

    this.highlightConstrainRelatedObjects();
    ctx2.viewer.requestRender();

    if (from !== PickFrom.ObjectTree) {
      userEvents.emit(ConstraintIconEvents.SelectFromCanvas, {
        constraints: this.selectedIcons.map((i) => i.constraintTag.constraint),
        from,
      });
    }
  }
}

export const constraintIconPicker = new ConstraintIconPicker();
